#pragma once

#include <vex/functional/make_raw_multicast_delegate.h>
#include <vex/functional/any_multicast_delegate.h>

namespace vex { namespace functional {

    namespace any {
        template<class TReducer> struct reducer_result {
            typedef typename std::decay<TReducer>::type reducer_type;
            typedef typename reducer_type::result_type result_type;
            typedef typename reducer_type::argument_type argument_type;

            typedef typename std::result_of<TReducer(argument_type)>::type type;
        };
    }

    /// <summary>
    /// Create an empty multicast delegate with default "void" reducer. 
    /// Applicable only if <c>TSignature</c> has <c>void</c> return type
    /// </summary>
    template<class TSignature> auto make_any_multicast_delegate()
        -> any_multicast_delegate<TSignature>
    {
        return vex::as_any_from_output() 
            | make_raw_multicast_delegate<TSignature>();
    }

    /// <summary>
    /// Create an empty multicast delegate with custom or default reducer. A valid reducer is a callable type
    /// which satisfies the <c>Reducer</c> concept. The created multicast delegate has a return type identical
    /// with the return type of the reducer. The reducer must define an <c>operator()(invoker_range of T*)</c> 
    /// where <c>T</c> is implicitely convertible from result type of <c>TSignature</c>
    /// </summary>
    ///
    /// <param name="p_reducer">Instance of a valid reducer type</param>
    template<class TSignature, class TReducer> auto make_any_multicast_delegate(TReducer&& p_reducer)
        -> any_multicast_delegate<
            TSignature, 
            typename any::reducer_result<TReducer>::type
        >
    {
        return vex::as_any_from_output()
            | make_raw_multicast_delegate<TSignature>(std::forward<TReducer>(p_reducer));
    }
}}